频率组件
只要继承了 APIView 就可以使用频率组件
组件的执行顺序: 认证 -> 权限 -> 频率
1. 频率类的固定写法
# rf_throttle.py
class 频率类的类名(object):
def allow_request(self, request, view):
# 频率的逻辑代码
if True / False:
return True
else:
return False
def wait(self):
# 钩子函数(即: 回调函数)
# 当超过设置的访问频率时调用(即: 当allow_request返回False时调用)
# 返回需要等待的时间
return '时间字符串'
2. 局部频率 -> 自己编写频率类
- 局部频率的配置(即: throttle_classes = [频率类, 频率类])会覆盖掉该视图类在全局频率中的配置
- throttle_classes = [频率类, 频率类]
# rf_throttle.py
import time
VISIT_RECORD = {
# '127.0.0.1:8080':[最新的时间,……,最初的时间]
}
class MyThrottle(object):
"""
一分钟允许访问5次
原理:
拿到用户IP地址
编写访问记录 {'ip地址':[最新的时间,……,最初的时间]}
确保用户所对应的记录列表的最新时间和最初时间的时间差 <= 规定时间
判断用户所对应的记录列表的长度 < 允许访问次数
"""
allow_time = 60 # 时间
visits = 5 # 允许访问次数
def __init__(self):
self.history_list = []
def allow_request(self, request, view):
"""
:param request: APIView 的 request
:param view: 视图类的实例化对象 -> APIView 类中的 self
:return: True/False
"""
# 获取用户的IP地址
ip = request.META.get('REMOTE_ADDR', '')
if ip not in VISIT_RECORD:
VISIT_RECORD[ip] = [time.time()]
else:
history = VISIT_RECORD[ip]
if len(history) <= self.visits:
history.insert(0, time.time()) # 将最新的时间添加到列表的第一位
self.history_list = history # 因为 history 是列表,所以 self.history_list = history 是引用关系,没有进行深度拷贝,所以下面 history.pop() 执行 self.history_list 无需重新赋值
while time.time() - history[-1] > self.allow_time:
# 确保列表时间是允许的范围内(即: 列表的时间差不能超过60秒)
# 循环计算最新的时间和最初的时间是否在60秒,如果不是删除则删除最初的时间
history.pop()
# 判断列表的长度有没有超过允许访问次数
if len(history) > self.visits:
return False
return True
def wait(self):
# 钩子函数(即: 回调函数)
# 当超过设置的访问频率时调用(即: 当allow_request返回False时调用)
# 返回值等待时间
return self.allow_time - (time.time() - self.history_list[-1])
# views.py
from .models import *
from .serializer import *
from .rf_auth import *
from .rf_permission import *
from .rf_throttle import *
from rest_framework import viewsets
from rest_framework import exceptions
# 修改频率组件的错误信息
class MyExceptions(exceptions.Throttled):
default_detail = '连接次数过多'
extra_detail_plural = extra_detail_singular = '请在{wait}秒后访问'
def __init__(self, wait=None, detail=None, code=None):
super().__init__(wait=wait, detail=detail, code=code)
class BookViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuth] # 局部认证,只作用于当前视图
permission_classes = [SVIPPermission] # 局部权限,只作用于当前视图
throttle_classes = [MyThrottle] # 局部频率,只作用于当前视图
queryset = Book.objects.all()
serializer_class = BookSerializers
# 修改频率组件的错误信息
def throttled(self, request, wait):
raise MyExceptions(wait=wait)
2. 全局频率 -> 自己编写频率类
- 全局频率作用于所有视图类
# rf_throttle.py
import time
VISIT_RECORD = {
# '127.0.0.1:8080':[最新的时间,……,最初的时间]
}
class MyThrottle(object):
"""
一分钟允许访问5次
原理:
拿到用户IP地址
编写访问记录 {'ip地址':[最新的时间,……,最初的时间]}
确保用户所对应的记录列表的最新时间和最初时间的时间差 <= 规定时间
判断用户所对应的记录列表的长度 < 允许访问次数
"""
allow_time = 60 # 时间
visits = 5 # 允许访问次数
def __init__(self):
self.history_list = []
def allow_request(self, request, view):
"""
:param request: APIView 的 request
:param view: 视图类的实例化对象 -> APIView 类中的 self
:return: True/False
"""
# 获取用户的IP地址
ip = request.META.get('REMOTE_ADDR', '')
if ip not in VISIT_RECORD:
VISIT_RECORD[ip] = [time.time()]
else:
history = VISIT_RECORD[ip]
if len(history) <= self.visits:
history.insert(0, time.time()) # 将最新的时间添加到列表的第一位
self.history_list = history # 因为 history 是列表,所以 self.history_list = history 是引用关系,没有进行深度拷贝,所以下面 history.pop() 执行 self.history_list 无需重新赋值
while time.time() - history[-1] > self.allow_time:
# 确保列表时间是允许的范围内(即: 列表的时间差不能超过60秒)
# 循环计算最新的时间和最初的时间是否在60秒,如果不是删除则删除最初的时间
history.pop()
# 判断列表的长度有没有超过允许访问次数
if len(history) > self.visits:
return False
return True
def wait(self):
# 钩子函数(即: 回调函数)
# 当超过设置的访问频率时调用(即: 当allow_request返回False时调用)
# 返回值等待时间
return self.allow_time - (time.time() - self.history_list[-1])
# settings.py
REST_FRAMEWORK = {
# "DEFAULT_AUTHENTICATION_CLASSES": ["app01.rf_auth.TokenAuth"], # 全局认证
# "DEFAULT_PERMISSION_CLASSES": ["app01.rf_permission.SVIPPermission"] # 全局权限
"DEFAULT_THROTTLE_CLASSES": ["app01.rf_throttle.MyThrottle"] # 全局频率
}
- 不进行频率认证的视图类设置
- 如果设置了全局频率,但是又想某些视图不进行频率认证,那么可以在不想进行频率认证的视图类中设置 throttle_classes = [],因为在 rest-framework 源码中 局部频率的配置 会覆盖掉 全局频率的配置
# views.py
import hashlib
import time
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *
from .rf_auth import *
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializers
class PublishViewSet(viewsets.ModelViewSet):
queryset = Publish.objects.all()
serializer_class = PublishSerializers
# 获取以用户名作为盐,以当前时间作为加密内容的字符串token
def get_token_str(username):
now_time = str(time.time())
md5 = hashlib.md5(bytes(username, encoding="utf8")) # 加盐,使用用户名作为盐,保证token的唯一性
md5.update(bytes(now_time, encoding='utf-8'))
return md5.hexdigest()
# 登陆,并且返回当前用户的token
class LoginView(APIView):
throttle_classes = [] # 在全局频率下,当前视图不进行频率认证
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
ret = {
'status': 0,
'msg': None
}
if user:
token_str = get_token_str(user.username) # 获取Token值
# 每次登陆成功后都要修改当前用户的token值
Token.objects.update_or_create(user=user, defaults={'token': token_str}) # 如果有 user=user 这条数据,那么就修改 token 值,否则就进行添加
ret['status'] = 1
ret['token'] = token_str
else:
ret['msg'] = '登陆失败'
return Response(ret)
4. 局部频率 -> 使用 rest-framework 所提供的频率类
# rf_throttle.py
from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
rate = '5/m' # 每分钟允许访问5次
# 获取用户IP地址
def get_cache_key(self, request, view):
return self.get_ident(request)
# views.py
from .rf_throttle import *
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
throttle_classes = [MyThrottle]
queryset = Book.objects.all()
serializer_class = BookSerializers
5. 全局频率 -> 使用 rest-framework 所提供的频率类
# rf_throttle.py
from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
scope = 'visit_rate' # 设置全局频率的属性名
# 获取用户IP地址
def get_cache_key(self, request, view):
return self.get_ident(request)
# settings.py
REST_FRAMEWORK = {
# "DEFAULT_AUTHENTICATION_CLASSES": ["app01.rf_auth.TokenAuth"], # 全局认证
# "DEFAULT_PERMISSION_CLASSES": ["app01.rf_permission.SVIPPermission"] # 全局权限
"DEFAULT_THROTTLE_CLASSES": ["app01.rf_throttle.MyThrottle"], # 全局频率
"DEFAULT_THROTTLE_RATES": {
'visit_rate': '5/m' # 每分钟允许访问5次
}
}
← 认证组件